/*
 * Copyright (c) 2012 Travis Geiselbrecht
 *
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file or at
 * https://opensource.org/licenses/MIT
 */
#include <lk/err.h>
#include <lk/debug.h>
#include <dev/uart.h>
#include <platform.h>
#include <platform/hc32.h>
#include <lib/watchdog.h>
#include <arch/arm/cm.h>
#include <hc32_ddl.h>
#include <hc32f46x.h>

void SystemClockConfig( void )
{
  en_clk_sys_source_t     enSysClkSrc;
  stc_clk_sysclk_cfg_t    stcSysClkCfg;
  stc_clk_xtal_cfg_t      stcXtalCfg;
  stc_clk_mpll_cfg_t      stcMpllCfg;
  stc_sram_config_t       stcSramConfig;

  MEM_ZERO_STRUCT(enSysClkSrc);
  MEM_ZERO_STRUCT(stcSysClkCfg);
  MEM_ZERO_STRUCT(stcXtalCfg);
  MEM_ZERO_STRUCT(stcMpllCfg);

  /* Set bus clk div. */
  stcSysClkCfg.enHclkDiv  = ClkSysclkDiv1;  /* Max 168MHz */
  stcSysClkCfg.enExclkDiv = ClkSysclkDiv2;  /* Max 84MHz */
  stcSysClkCfg.enPclk0Div = ClkSysclkDiv1;  /* Max 168MHz */
  stcSysClkCfg.enPclk1Div = ClkSysclkDiv2;  /* Max 84MHz */
  stcSysClkCfg.enPclk2Div = ClkSysclkDiv4;  /* Max 60MHz */
  stcSysClkCfg.enPclk3Div = ClkSysclkDiv4;  /* Max 42MHz */
  stcSysClkCfg.enPclk4Div = ClkSysclkDiv2;  /* Max 84MHz */
  CLK_SysClkConfig(&stcSysClkCfg);

  /* Switch system clock source to MPLL. */
  /* Use Xtal as MPLL source. */
  stcXtalCfg.enMode = ClkXtalModeOsc;
  stcXtalCfg.enDrv = ClkXtalLowDrv;
  stcXtalCfg.enFastStartup = Enable;
  CLK_XtalConfig(&stcXtalCfg);
  CLK_XtalCmd(Enable);

  /* Set MPLL out 168MHz. */
  stcMpllCfg.pllmDiv = 1u;
  /* sysclk = 8M / pllmDiv * plln / PllpDiv */
  stcMpllCfg.plln    = 42u;
  stcMpllCfg.PllpDiv = 2u;
  stcMpllCfg.PllqDiv = 2u;
  stcMpllCfg.PllrDiv = 2u;
  
  CLK_SetPllSource(ClkPllSrcXTAL);
  CLK_MpllConfig(&stcMpllCfg);

  /* flash read wait cycle setting */
  EFM_Unlock();
  EFM_SetLatency(EFM_LATENCY_4);
  EFM_Lock();

  /* If the system clock frequency is higher than 100MHz and SRAM1, SRAM2, SRAM3 or Ret_SRAM is used,
     the wait cycle must be set. */
  stcSramConfig.u8SramIdx     = Sram12Idx | Sram3Idx | SramRetIdx;
  stcSramConfig.enSramRC      = SramCycle2;
  stcSramConfig.enSramWC      = SramCycle2;
  stcSramConfig.enSramEccMode = EccMode0;
  stcSramConfig.enSramEccOp   = SramNmi;
  stcSramConfig.enSramPyOp    = SramNmi;
  SRAM_Init(&stcSramConfig);

  /* Enable MPLL. */
  CLK_MpllCmd(Enable);

  /* Wait MPLL ready. */
  while(Set != CLK_GetFlagStatus(ClkFlagMPLLRdy));

  /* Switch system clock source to MPLL. */
  CLK_SetSysClkSource(CLKSysSrcMPLL);
}



void platform_early_init(void) {
	stc_sram_config_t SramConfig;
    // Do general system init
	MEM_ZERO_STRUCT(SramConfig);
	SramConfig.u8SramIdx = Sram3Idx;
	SramConfig.enSramRC = SramCycle2;
	SramConfig.enSramWC = SramCycle2;
	SRAM_Init(&SramConfig);
	// Do system clock config
    SystemClockConfig();

    // start the systick timer
    stc_clk_freq_t clocks;
    CLK_GetClockFreq(&clocks);
    arm_cm_systick_init(clocks.sysclkFreq);

    hc32_timer_early_init();
    hc32_gpio_early_init();
}

void platform_init(void) {
    hc32_timer_init();
	watchdog_hw_init(500);
	watchdog_hw_set_enabled(true);
}
